Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Commit 8a2f357c49cc736f6efae76cd8568c14000f96a1


Parents : 82d291e
Author : Ivan <ivan@quad4.io>
Signature : Signature validation error
Date : 2026-04-16T03:20:55-05:00

feat(tests): add tests for app info endpoint handling missing runtime objects and package version resolution without packaging module

Changes

2 files changed, 65 insertions(+), 0 deletions(-)


Diff

diff --git a/tests/backend/test_app_endpoints.py b/tests/backend/test_app_endpoints.py
index 823d78e2..b2d1d598 100644
--- a/tests/backend/test_app_endpoints.py
+++ b/tests/backend/test_app_endpoints.py
@@ -128,3 +128,50 @@ async def test_app_shutdown_endpoint(mock_rns_minimal, temp_dir):
# Since it's in a task, we might need to check if it was called
# but sys.exit might not have been reached yet or was called in a different context
# For this test, verifying the endpoint exists and returns 200 is sufficient.
+
+
+@pytest.mark.asyncio
+async def test_app_info_tolerates_missing_runtime_objects(mock_rns_minimal, temp_dir):
+ with (
+ patch("meshchatx.meshchat.generate_ssl_certificate"),
+ patch("psutil.Process") as mock_process,
+ patch("psutil.net_io_counters", side_effect=PermissionError),
+ patch("importlib.metadata.version", side_effect=Exception),
+ patch("importlib.metadata.distribution", side_effect=Exception),
+ patch("importlib.metadata.packages_distributions", return_value={}),
+ patch("importlib.import_module", side_effect=Exception),
+ ):
+ mock_proc_instance = mock_process.return_value
+ mock_proc_instance.memory_info.side_effect = PermissionError
+ mock_proc_instance.net_connections.side_effect = PermissionError
+
+ app_instance = ReticulumMeshChat(
+ identity=mock_rns_minimal,
+ storage_dir=temp_dir,
+ reticulum_config_dir=temp_dir,
+ )
+ app_instance.database = None
+ app_instance.config = None
+
+ request = MagicMock()
+ app_info_handler = None
+ for route in app_instance.get_routes():
+ if route.path == "/api/v1/app/info" and route.method == "GET":
+ app_info_handler = route.handler
+ break
+ assert app_info_handler is not None
+
+ response = await app_info_handler(request)
+ assert response.status == 200
+ data = json.loads(response.body)
+ info = data["app_info"]
+
+ assert info["database_file_size"] == 0
+ assert info["database_files"]["total_bytes"] == 0
+ assert info["memory_usage"]["rss"] == 0
+ assert info["memory_usage"]["vms"] == 0
+ assert info["network_stats"]["bytes_sent"] == 0
+ assert info["network_stats"]["bytes_recv"] == 0
+ assert info["sqlite"]["journal_mode"] == "unknown"
+ assert info["tutorial_seen"] is False
+ assert info["changelog_seen_version"] == "0.0.0"

diff --git a/tests/backend/test_package_version_resolution.py b/tests/backend/test_package_version_resolution.py
index f9b48ff5..4da9ea68 100644
--- a/tests/backend/test_package_version_resolution.py
+++ b/tests/backend/test_package_version_resolution.py
@@ -88,3 +88,21 @@ def test_app_info_dependency_keys_resolve_in_dev_env(package: str):
def test_audioop_lts_resolves_when_applicable():
v = ReticulumMeshChat.get_package_version("audioop-lts")
assert v != "unknown"
+
+
+def test_get_package_version_works_without_packaging_module():
+ real_import = __import__
+
+ def _import(name, globals=None, locals=None, fromlist=(), level=0):
+ if name.startswith("packaging"):
+ raise ModuleNotFoundError("No module named 'packaging'")
+ return real_import(name, globals, locals, fromlist, level)
+
+ with (
+ patch("builtins.__import__", side_effect=_import),
+ patch("importlib.metadata.version", side_effect=importlib.metadata.PackageNotFoundError),
+ patch("importlib.metadata.distribution", side_effect=importlib.metadata.PackageNotFoundError),
+ patch("importlib.metadata.packages_distributions", return_value={}),
+ patch("importlib.import_module", side_effect=Exception),
+ ):
+ assert ReticulumMeshChat.get_package_version("lxst", "fallback") == "fallback"


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────